package cn.csc.module.pay.controller.app.notify;

import cn.csc.framework.common.util.json.JsonUtils;
import cn.csc.framework.operatelog.core.annotations.OperateLog;
import cn.csc.framework.pay.core.client.PayClient;
import cn.csc.framework.pay.core.client.dto.PayNotifyReqDTO;
import cn.csc.framework.pay.core.client.dto.PayOrderUnifiedRespDTO;
import cn.csc.framework.pay.core.client.dto.PayRefundUnifiedRespDTO;
import cn.csc.framework.pay.core.enums.channel.PayChannelEnum;
import cn.csc.framework.tenant.core.context.TenantContextHolder;
import cn.csc.module.pay.dal.dataobject.merchant.PayChannelDO;
import cn.csc.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.csc.module.pay.service.merchant.PayChannelService;
import cn.csc.module.pay.service.order.PayOrderExtensionService;
import cn.csc.module.pay.service.order.PayOrderService;
import cn.csc.module.pay.service.refund.PayRefundService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import java.math.BigInteger;
import java.util.Map;

import static cn.csc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csc.framework.common.util.json.JsonUtils.toJsonString;
import static cn.csc.module.pay.enums.ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND;

/**
 * @Author：云隐
 * @Date：2023/3/21 9:52
 */
@Api(tags = "用户 APP  - 支付通知")
@RestController
@RequestMapping("/pay/notify")
@Validated
@Slf4j
public class AppPayNotifyController {

    @Resource
    private PayOrderService orderService;
    @Resource
    private PayRefundService refundService;

    @Resource
    private PayChannelService channelService;

    @Resource
    private PayOrderExtensionService payOrderExtensionService;

    /**
     * 统一的跳转页面，支付宝跳转参数说明
     *
     * <a href="https://opendocs.alipay.com/open/203/105285#前台回跳参数说明">支付宝 - 前台回跳参数说明</a>
     *
     * @param channelId 渠道编号
     * @return 返回跳转页面
     */
    @GetMapping(value = "/return/{channelId}")
    @ApiOperation(value = "渠道统一的支付成功返回地址")
    @Deprecated // TODO 如果是 way 的情况，应该是跳转回前端地址
    public String returnCallback(@PathVariable("channelId") Long channelId,
                                 @RequestParam Map<String, String> params) {
        log.info("[returnCallback][app_id({}) 跳转]", params.get("app_id"));
        return String.format("渠道[%s]支付成功", channelId);
    }

    /**
     * 统一的渠道支付回调，支付宝的退款回调
     *
     * @param channelId 渠道编号
     * @param params form 参数
     * @param body request body
     * @return 成功返回 "success"
     */
    @PostMapping(value = "/callback/{channelId}")
    @ApiOperation(value = "支付渠道的统一回调接口 - 包括支付回调，退款回调")
    @PermitAll
    @OperateLog(enable = false) // 回调地址，无需记录操作日志
    public String notifyCallback(@PathVariable("channelId") BigInteger channelId,
                                 @RequestParam(required = false) Map<String, String> params,
                                 @RequestBody(required = false) String body) throws InterruptedException {
        log.info("[appNotifyCallback][channelId({}) 回调数据({} / {})]", channelId, params, body);
        // 1. 校验支付渠道是否存在
        PayClient payClient = channelService.getPayClient(channelId);
        if (payClient == null) {
            log.error("[appNotifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
            throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
        }

        // 2. 解析通知数据
        PayNotifyReqDTO rawNotify = PayNotifyReqDTO.builder().params(params).body(body).build();
        Object notify = payClient.parseNotify(rawNotify);
        log.info("[appNotifyCallback]notify: {}", JsonUtils.toJsonString(notify));

        PayChannelDO channel = channelService.getChannel(channelId);
        PayChannelEnum channelEnum = PayChannelEnum.getByCode(channel.getCode());
        // 3. 处理通知
        // 3.1：退款通知
        if (notify instanceof PayRefundUnifiedRespDTO) {
            // TODO 设置租户ID
            // notifyRefund没出异常即为回调成功
            refundService.notifyRefund(channelId, (PayRefundUnifiedRespDTO) notify);
            /* 拉卡拉的通知返回格式为：{"code":"SUCCESS","message":"执行成功"}
               参见：http://open.lakala.com/#/home/document/detail?title=%E6%8E%A5%E5%85%A5%E6%B5%81%E7%A8%8B&id=117
               通联：https://aipboss.allinpay.com/know/devhelp/main.php?pid=15&from=groupmessage&isappinstalled=0#mid=94
               不过本地通知信息的更新为异步, 等待1500ms再查询通知任务状态
             */
            switch (channelEnum){
                case LKL_QR:
                case LKL_SCAN_QR:
                    return "{\"code\": \"SUCCESS\", \"message\": \"执行成功\"}";
                default:
                    return "success";
            }
        }
        // 3.2：支付通知
        if (notify instanceof PayOrderUnifiedRespDTO) {
            // 设置租户ID
            TenantContextHolder.setIgnore(true);
            PayOrderExtensionDO extensionByNo = payOrderExtensionService.getOrderExtensionByNo(((PayOrderUnifiedRespDTO) notify).getTradeNo());
            log.info("[appNotifyCallback]extensionByNo: {}", extensionByNo);
            TenantContextHolder.setIgnore(false);
            TenantContextHolder.setTenantId(extensionByNo.getTenantId());
            log.info("[appNotifyCallback]TenantId: {}", TenantContextHolder.getTenantId());
            /*  通联通知：商户服务端在接收到结果通知并正确处理后，应响应消息。正确处理响应success，其他响应或者不响应通联开发平台则通过重发机
                        制进行重发，重发次数为5次
                拉卡拉响应报文：{"code": "SUCCESS", "message": "执行成功"}。 https://o.lakala.com/#/home/document/detail?title=%E6%8E%A5%E5%85%A5%E8%A7%84%E8%8C%83&id=117
             */
            Boolean notifyResult = orderService.notifyOrder(channelId, (PayOrderUnifiedRespDTO) notify);
            switch (channelEnum){
                case LKL_QR:
                case LKL_SCAN_QR:
                    return notifyResult ? "{\"code\": \"SUCCESS\", \"message\": \"执行成功\"}" : "fail";
                default:
                    return notifyResult ? "success" : "fail";
            }

        }
        throw new UnsupportedOperationException("未知通知：" + toJsonString(notify));
    }


}
